--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Node / ReticulumProjects / MeshChatX.git / files / vendor / lxmfy / docs / source / creating-bots.rst
Displaying Raw • Download
vendor/lxmfy/docs/source/creating-bots.rst bf4da5d770806e03cde3b0a11bc7361cc0a8b55c (bf4da5d7) Text, 23.69 KB
Tc9d1d9Creating Bots
Tc9d1d9=============
Tc9d1d9Basic Structure
Tc9d1d9---------------
A minimal LXMFy bot involves:
T79c0ff1. Importing Te6edf3:code:Te6edf3`LXMFBot`.
T79c0ff2. Instantiating Te6edf3:code:Te6edf3`LXMFBot` with desired configuration.
T79c0ff3. Defining commands or event handlers.
T79c0ff4. Running the bot using Te6edf3:code:Te6edf3`bot.run()`.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
T8b949e# 1. Instantiate the bot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffSimpleBotTa5d6ff"Tb4b4b4,
Te6edf3command_prefixTff7b72=Ta5d6ff"Ta5d6ff!Ta5d6ff"Tb4b4b4,
Te6edf3storage_pathTff7b72=Ta5d6ff"Ta5d6ffsimple_dataTa5d6ff"
Tb4b4b4)
T8b949e# 2. Define commands
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffpingTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffResponds with pongTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffping_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
T8b949e# ctx is a context object containing message info
T8b949e# ctx.sender: Sender's LXMF hash
T8b949e# ctx.content: Full message content
T8b949e# ctx.args: List of arguments after the command
T8b949e# ctx.reply(message): Function to send a reply
T8b949e# (can also take keyword arguments like title="My Title", lxmf_fields=some_fields)
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffPong!Ta5d6ff"Tb4b4b4)
T8b949e# For long-running tasks, you can use threaded commands:
T8b949e# import time
T8b949e# @bot.command(name="long_op", description="Performs a long operation in a separate thread", threaded=True)
T8b949e# def long_op_command(ctx):
T8b949e# ctx.reply("Starting long operation...")
T8b949e# time.sleep(10) # Simulate a long-running operation
T8b949e# ctx.reply("Long operation complete!")
T8b949e# Important: Threaded commands should not directly interact with RNS or lxmfy.transport.py.
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffgreetTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffGreets the userTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffgreet_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3ctxTff7b72.Td2a8ffargsTb4b4b4:
Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ff Ta5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffargsTb4b4b4)
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffHello, Tffd700{Te6edf3nameTffd700}Ta5d6ff!Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffHello there! Tell me your name: !greet <your_name>Ta5d6ff"Tb4b4b4)
T8b949e# 3. Run the bot
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffStarting bot: Tffd700{Te6edf3botTff7b72.Td2a8ffconfigTff7b72.Td2a8ffnameTffd700}Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffBot LXMF Address: Tffd700{Te6edf3botTff7b72.Td2a8fflocalTff7b72.Td2a8ffhashTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
Tc9d1d9Using Templates
Tc9d1d9---------------
LXMFy provides several templates for common bot types. You can use the CLI to generate a bot file based on a template.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72bash
T8b949e# Create an echo bot
lxmfy create --template Tffa657echo my_echo_bot
T8b949e# Create a reminder bot (uses SQLite storage)
lxmfy create --template reminder my_reminder_bot
T8b949e# Create a note-taking bot (uses JSON storage)
lxmfy create --template note my_note_bot
T8b949e# Create a cog test bot (tests cog loading features)
lxmfy create --template cogtest my_cog_test_bot
Running these commands creates a Python file (e.g., Te6edf3:code:Te6edf3`my_echo_bot.py`) that imports and runs the chosen template. You can then modify the generated file or the template code itself (Te6edf3:code:Te6edf3`lxmfy/templates/...`).
**Example generated file (:code:`my_cog_test_bot.py`):**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfyT7ee787.T7ee787templates Tff7b72import Te6edf3CogTestBot
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3bot Tff7b72= Te6edf3CogTestBotTb4b4b4(Tb4b4b4) T8b949e# Creates an instance of the CogTestBot template
T8b949e# You can optionally override the default name:
T8b949e# bot.bot.name = "My Cog Test Bot"
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
Tc9d1d9Bot Configuration
Tc9d1d9-----------------
When creating an Te6edf3:code:Te6edf3`LXMFBot` instance, you can pass various keyword arguments to configure its behavior. See the Te6edf3:code:Te6edf3`BotConfig` section in the Ta5d6ff`API Reference Tffd700<api-reference.html>Ta5d6ff`_ or the Ta5d6ff`Quick Start Guide Tffd700<quick-start.html>Ta5d6ff`_ for a list of common options.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffConfiguredBotTa5d6ff"Tb4b4b4,
Te6edf3announceTff7b72=T79c0ff3600Tb4b4b4, T8b949e# Announce every hour
Te6edf3adminsTff7b72=Tb4b4b4{Ta5d6ff"Ta5d6ffyour_admin_hash_hereTa5d6ff"Tb4b4b4}Tb4b4b4, T8b949e# Set admin user(s)
Te6edf3command_prefixTff7b72=Ta5d6ff"Ta5d6ff$Ta5d6ff"Tb4b4b4, T8b949e# Use '$' as prefix
Te6edf3storage_typeTff7b72=Ta5d6ff"Ta5d6ffsqliteTa5d6ff"Tb4b4b4, T8b949e# Use SQLite database
Te6edf3storage_pathTff7b72=Ta5d6ff"Ta5d6ffdata/my_bot_data.dbTa5d6ff"Tb4b4b4, T8b949e# Specify DB file path
Te6edf3rate_limitTff7b72=T79c0ff10Tb4b4b4, T8b949e# Allow 10 messages / minute
Te6edf3cooldownTff7b72=T79c0ff30Tb4b4b4, T8b949e# Cooldown of 30 seconds
Te6edf3permissions_enabledTff7b72=Tff7b72True T8b949e# Enable role-based permissions
Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
T8b949e# You can also modify config after instantiation
T8b949e# Note: some settings are best set during init
Te6edf3botTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmax_warnings Tff7b72= T79c0ff5
Te6edf3botTff7b72.Td2a8ffspam_protectionTff7b72.Td2a8ffconfigTff7b72.Td2a8ffmax_warnings Tff7b72= T79c0ff5 T8b949e# Update spam protector too
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
Tc9d1d9Setting a Bot Icon (LXMF Field)
Tc9d1d9^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can give your bot a custom icon that appears in compatible LXMF clients. This uses the Te6edf3:code:Te6edf3`LXMF.FIELD_ICON_APPEARANCE` and can be set when sending messages.
First, ensure you have the necessary imports:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3IconAppearanceTb4b4b4, Te6edf3pack_icon_appearance_field
Then, you can define and use the icon:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
T8b949e# In your bot class or setup
Te6edf3icon_data Tff7b72= Te6edf3IconAppearanceTb4b4b4(
Te6edf3icon_nameTff7b72=Ta5d6ff"Ta5d6ffrobot_2Ta5d6ff"Tb4b4b4, T8b949e# Choose from Material Symbols
Te6edf3fg_colorTff7b72=Ta5d6ffbTa5d6ff'Tffea00\x00Tffea00\xFFTffea00\x00Ta5d6ff'Tb4b4b4, T8b949e# Green
Te6edf3bg_colorTff7b72=Ta5d6ffbTa5d6ff'Tffea00\x33Tffea00\x33Tffea00\x33Ta5d6ff' T8b949e# Dark Grey
Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffbot_icon_field Tff7b72= Te6edf3pack_icon_appearance_fieldTb4b4b4(Te6edf3icon_dataTb4b4b4)
T8b949e# When sending a message or replying:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffMessage from your bot!Ta5d6ff"Tb4b4b4, Te6edf3lxmf_fieldsTff7b72=Tff7b72selfTff7b72.Td2a8ffbot_icon_fieldTb4b4b4)
T8b949e# or
T8b949e# bot.send(destination, "Another message", lxmf_fields=self.bot_icon_field)
This Te6edf3:code:Te6edf3`self.bot_icon_field` can be pre-calculated and reused for all messages sent by the bot.
Tc9d1d9Structured Commands via LXMF Fields
Tc9d1d9-----------------------------------
In addition to text-based commands, LXMFy supports commands sent through LXMF message fields using Ta5d6ff``Ta5d6ffFIELD_COMMANDSTa5d6ff`` (Ta5d6ff``Ta5d6ff0x09Ta5d6ff``). This is useful for structured request/response workflows between LXMF clients and bots.
When a message contains Ta5d6ff``Ta5d6ffFIELD_COMMANDSTa5d6ff``, the bot extracts the command name and arguments, routes them through the same command registry as text commands, and automatically includes Ta5d6ff``Ta5d6ffFIELD_RESULTSTa5d6ff`` (Ta5d6ff``Ta5d6ff0x0ATa5d6ff``) in the reply.
**Receiving structured commands**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffFieldBotTa5d6ff"Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffaddTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffAdd two numbersTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffadd_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657lenTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffargsTb4b4b4) Tff7b72>Tff7b72= T79c0ff2Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3result Tff7b72= Tffa657floatTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffargsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4) Tff7b72+ Tffa657floatTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffargsTb4b4b4[T79c0ff1Tb4b4b4]Tb4b4b4)
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Tffa657strTb4b4b4(Te6edf3resultTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffInvalid numbersTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffUsage: add <a> <b>Ta5d6ff"Tb4b4b4)
The Ta5d6ff``Ta5d6ffctxTa5d6ff`` object in field command callbacks includes:
T79c0ff- Te6edf3:code:Te6edf3`ctx.fields` — the raw LXMF fields dict from the incoming message
T79c0ff- Te6edf3:code:Te6edf3`ctx.request_id` — the Ta5d6ff``Ta5d6ffrequest_idTa5d6ff`` from the incoming Ta5d6ff``Ta5d6ffFIELD_COMMANDSTa5d6ff`` (if any)
**Sending a structured command from an LXMF client**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72import T7ee787LXMF
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3FIELD_COMMANDS
Te6edf3lxm Tff7b72= Te6edf3LXMFTff7b72.Td2a8ffLXMessageTb4b4b4(
Te6edf3destinationTb4b4b4,
Te6edf3sourceTb4b4b4,
Ta5d6ffbTa5d6ff"Ta5d6ff"Tb4b4b4, T8b949e# content can be empty for field-only commands
Te6edf3desired_methodTff7b72=Te6edf3LXMFTff7b72.Td2a8ffLXMessageTff7b72.Td2a8ffDIRECTTb4b4b4,
Tb4b4b4)
Te6edf3lxmTff7b72.Td2a8fffieldsTb4b4b4[Te6edf3FIELD_COMMANDSTb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffcommandTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffaddTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffargsTa5d6ff"Tb4b4b4: Tb4b4b4[Ta5d6ff"Ta5d6ff3Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff5Ta5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffrequest_idTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffreq-42Ta5d6ff"Tb4b4b4, T8b949e# optional, for correlation
Tb4b4b4}
Te6edf3routerTff7b72.Td2a8ffhandle_outboundTb4b4b4(Te6edf3lxmTb4b4b4)
**Disabling field commands**
If you want the bot to ignore Ta5d6ff``Ta5d6ffFIELD_COMMANDSTa5d6ff`` and only process text commands, set:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffTextOnlyBotTa5d6ff"Tb4b4b4,
Te6edf3lxmf_commands_enabledTff7b72=Tff7b72FalseTb4b4b4,
Tb4b4b4)
Tc9d1d9Using Cogs (Extensions)
Tc9d1d9-----------------------
Cogs allow you to organize your commands and event listeners into separate files (modules), keeping your main bot file cleaner.
T79c0ff1. **Create a :code:`cogs` directory** (or whatever you set Te6edf3:code:Te6edf3`cogs_dir` to in Te6edf3:code:Te6edf3`BotConfig`).
T79c0ff2. **Create Python files** inside the Te6edf3:code:Te6edf3`cogs` directory (e.g., Te6edf3:code:Te6edf3`utility.py`).
T79c0ff3. **Define a class** that inherits from Te6edf3:code:Te6edf3`lxmfy.Cog` (optional but good practice) or is just a standard class.
T79c0ff4. **Define commands** as methods within the class using the Te6edf3:code:Te6edf3`@Command` decorator.
T79c0ff5. **Create a :code:`setup(bot)` function** in the cog file, which LXMFy will call to register the cog.
**Example (:code:`cogs/utility.py`):**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3Command
Tff7b72from T7ee787lxmfyT7ee787.T7ee787commands Tff7b72import Te6edf3Cog T8b949e# Import Cog if inheriting
Tff7b72import T7ee787time
Tff7b72class T56d364UtilityCogTb4b4b4: T8b949e# Or class UtilityCog(Cog):
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3botTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffbot Tff7b72= Te6edf3bot
Tff7b72selfTff7b72.Td2a8ffstart_time Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tf0883e@CommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffuptimeTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffShows bot uptimeTa5d6ff"Tb4b4b4)
T8b949e# Note: Methods in cogs often take 'self' and 'ctx'
Tff7b72def Td2a8ffuptime_commandTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3uptime_seconds Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72- Tff7b72selfTff7b72.Td2a8ffstart_time
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffBot has been running for Tffd700{Te6edf3uptime_secondsTffd700:Ta5d6ff.2fTffd700}Ta5d6ff seconds.Ta5d6ff"Tb4b4b4)
Tf0883e@CommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffinfoTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffShows bot infoTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffinfo_commandTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3info Tff7b72= Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffBot Name: Tffd700{Tff7b72selfTff7b72.Td2a8ffbotTff7b72.Td2a8ffconfigTff7b72.Td2a8ffnameTffd700}Tffea00\nTa5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ffOwner(s): Tffd700{Ta5d6ff'Ta5d6ff, Ta5d6ff'Tff7b72.Td2a8ffjoinTb4b4b4(Tff7b72selfTff7b72.Td2a8ffbotTff7b72.Td2a8ffconfigTff7b72.Td2a8ffadminsTb4b4b4) Tff7b72or Ta5d6ff'Ta5d6ffNoneTa5d6ff'Tffd700}Tffea00\nTa5d6ff"
Ta5d6fffTa5d6ff"Ta5d6ffPrefix: Tffd700{Tff7b72selfTff7b72.Td2a8ffbotTff7b72.Td2a8ffconfigTff7b72.Td2a8ffcommand_prefixTffd700}Ta5d6ff"
Tb4b4b4)
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Te6edf3infoTb4b4b4)
Tf0883e@CommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffthreaded_cog_taskTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffPerforms a long task in a cog threadTa5d6ff"Tb4b4b4, Te6edf3threadedTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72def Td2a8ffthreaded_cog_taskTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffStarting a long cog task... this will run in a separate thread.Ta5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff7Tb4b4b4) T8b949e# Simulate a long-running operation
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffLong cog task completed!Ta5d6ff"Tb4b4b4)
T8b949e# This function is required for the cog to be loaded
Tff7b72def Td2a8ffsetupTb4b4b4(Te6edf3botTb4b4b4)Tb4b4b4:
Te6edf3cog_instance Tff7b72= Te6edf3UtilityCogTb4b4b4(Te6edf3botTb4b4b4)
Te6edf3botTff7b72.Td2a8ffadd_cogTb4b4b4(Te6edf3cog_instanceTb4b4b4) T8b949e# Register the cog instance with the bot
**Main Bot File (:code:`my_bot.py`):**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffCogBotTa5d6ff"Tb4b4b4,
Te6edf3cogs_enabledTff7b72=Tff7b72TrueTb4b4b4, T8b949e# Make sure cogs are enabled (default)
Te6edf3cogs_dirTff7b72=Ta5d6ff"Ta5d6ffcogsTa5d6ff" T8b949e# Point to the directory
Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
T8b949e# Cogs are loaded automatically during LXMFBot initialization
T8b949e# if cogs_enabled is True.
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
When the bot starts, it will automatically find Te6edf3:code:Te6edf3`utility.py`, call its Te6edf3:code:Te6edf3`setup` function, which creates an instance of Te6edf3:code:Te6edf3`UtilityCog` and registers it using Te6edf3:code:Te6edf3`bot.add_cog()`. The commands defined in the cog (Te6edf3:code:Te6edf3`uptime`, Te6edf3:code:Te6edf3`info`) will then be available.
Tc9d1d9External Script Cogs (Multi-Language Support)
Tc9d1d9---------------------------------------------
You can also write bot extensions in languages other than Python (e.g., Bash, Ruby, Perl, Go, C) using External Script Cogs.
T79c0ff1. **Create an executable script** in your Te6edf3:code:Te6edf3`cogs` directory.
T79c0ff2. **Add a shebang** at the top of the script (e.g., Te6edf3:code:Te6edf3`#!/bin/bash`).
T79c0ff3. **Ensure the script is executable** (Te6edf3:code:Te6edf3`chmod +x your_script`).
When the bot starts, it will automatically register any executable file in the Te6edf3:code:Te6edf3`cogs` directory (that doesn't end in Te6edf3:code:Te6edf3`.py`) as a bot command.
**Argument Protocol:**
T79c0ff- Te6edf3:code:Te6edf3`$1`: Sender's LXMF hash.
T79c0ff- Te6edf3:code:Te6edf3`$2`: Full message content.
T79c0ff- Te6edf3:code:Te6edf3`$3`, Te6edf3:code:Te6edf3`$4`, ...: Individual command arguments.
**Environment Variables:**
T79c0ff- Te6edf3:code:Te6edf3`LXMFY_SENDER`: The sender's identity hash.
T79c0ff- Te6edf3:code:Te6edf3`LXMFY_CONTENT`: The full message content.
T79c0ff- Te6edf3:code:Te6edf3`LXMFY_HAS_ADMIN`: Te6edf3:code:Te6edf3`true` or Te6edf3:code:Te6edf3`false` depending on the sender's admin status.
**Example Bash Cog (:code:`cogs/greet.sh`):**
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72bash
T8b949e#!/bin/bash
Tffa657echo Ta5d6ff"Ta5d6ffHello from Bash! You sent: Te6edf3$2Ta5d6ff"
When a user sends Te6edf3:code:Te6edf3`/greet hello`, the bot will execute this script and reply with its stdout: Te6edf3:code:Te6edf3`Hello from Bash! You sent: /greet hello`.
Tc9d1d9Sovereign NLP (Local Intent Classification)
Tc9d1d9-------------------------------------------
LXMFy includes a built-in, lightweight NLP engine for intent classification. This allows your bot to understand the "intent" of a message even if it doesn't match a command exactly.
T79c0ff1. **Enable NLP** in your bot configuration: Te6edf3:code:Te6edf3`nlp_enabled=True`.
T79c0ff2. **Define intents** using the Te6edf3:code:Te6edf3`@bot.intent` decorator.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tf0883e@botTff7b72.Td2a8ffintentTb4b4b4(Ta5d6ff"Ta5d6ffhelpTa5d6ff"Tb4b4b4, Te6edf3examplesTff7b72=Tb4b4b4[Ta5d6ff"Ta5d6ffhow do I use this?Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffshow me commandsTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhelp me pleaseTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72def Td2a8ffhelp_intentTb4b4b4(Te6edf3msgTb4b4b4)Tb4b4b4:
Te6edf3msgTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffI can help! Try typing /help to see a list of commands.Ta5d6ff"Tb4b4b4)
The NLP engine uses mathematical vector similarity (TF-IDF and Cosine Similarity) to match incoming text against your example phrases. This processing happens entirely locally on your machine, ensuring full privacy.
**Persistence and Extensibility:**
For larger bots, you can export and import the trained intent model to avoid retraining on every startup:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
T8b949e# Export the model
Te6edf3model_data Tff7b72= Te6edf3botTff7b72.Td2a8ffnlpTff7b72.Td2a8ffexport_modelTb4b4b4(Tb4b4b4)
T8b949e# Save model_data to a file or database
T8b949e# Later, import it back
Te6edf3botTff7b72.Td2a8ffnlpTff7b72.Td2a8ffimport_modelTb4b4b4(Te6edf3model_dataTb4b4b4)
Tc9d1d9RNS Link Support
Tc9d1d9----------------
Bots can now establish and respond to direct RNS Links. This is useful for stateful, streaming, or high-bandwidth communication that goes beyond simple message packets.
T79c0ff1. **Enable Link Support** in configuration: Te6edf3:code:Te6edf3`link_support_enabled=True`.
T79c0ff2. **Request a link**: Te6edf3:code:Te6edf3`bot.request_link(destination_hash)`. You can also specify a custom app name and aspects: Te6edf3:code:Te6edf3`bot.request_link(dest, callback, "my_app", "aspect1")`.
T79c0ff3. **Handle incoming links**: Register a callback with Te6edf3:code:Te6edf3`bot.on_link(handler)`.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72def Td2a8ffhandle_linkTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffLink established with Tffd700{Te6edf3RNSTff7b72.Td2a8ffhexrepTb4b4b4(Te6edf3linkTff7b72.Td2a8ffdestinationTff7b72.Td2a8ffhashTb4b4b4)Tffd700}Ta5d6ff"Tb4b4b4)
T8b949e# You can now use the link for direct RNS communication
Te6edf3botTff7b72.Td2a8ffon_linkTb4b4b4(Te6edf3handle_linkTb4b4b4)
**Safety & Sandboxing:**
T79c0ff- **Timeouts:** External cogs have a default timeout (30s) to prevent hanging. This is configurable via Te6edf3:code:Te6edf3`external_cogs_timeout`.
T79c0ff- **Threading:** All external cogs run in separate threads and do not block the bot.
T79c0ff- **Sandboxing (Linux only):** If Te6edf3:code:Te6edf3`bubblewrap` (Te6edf3:code:Te6edf3`bwrap`) or Te6edf3:code:Te6edf3`firejail` is installed, the bot can automatically run scripts in a restricted, read-only sandbox. This is enabled by default via Te6edf3:code:Te6edf3`external_cogs_sandbox_enabled`.
Tc9d1d9Handling Messages
Tc9d1d9-----------------
LXMFy provides several ways to handle incoming messages at different stages of processing.
Tc9d1d9First Message Handler
Tc9d1d9^^^^^^^^^^^^^^^^^^^^^
Handle the first message from each new user (useful for welcome messages):
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffWelcomeBotTa5d6ff"Tb4b4b4,
Te6edf3first_message_enabledTff7b72=Tff7b72True T8b949e# Must be True (default)
Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffon_first_messageTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffwelcome_new_userTb4b4b4(Te6edf3senderTb4b4b4, Te6edf3messageTb4b4b4)Tb4b4b4:
Te6edf3content Tff7b72= Te6edf3messageTff7b72.Td2a8ffcontentTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3botTff7b72.Td2a8ffsendTb4b4b4(
Te6edf3senderTb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffWelcome to the bot! You said: Tffd700{Te6edf3contentTffd700}Tffea00\nTffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ffType /help to see available commands.Ta5d6ff"
Tb4b4b4)
Tff7b72return Tff7b72True T8b949e# Return True to stop further processing of this message
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
Tc9d1d9General Message Handler
Tc9d1d9^^^^^^^^^^^^^^^^^^^^^^^
Handle all incoming messages before command processing:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffEchoBotTa5d6ff"Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffon_messageTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffecho_non_commandsTb4b4b4(Te6edf3senderTb4b4b4, Te6edf3messageTb4b4b4)Tb4b4b4:
Te6edf3content Tff7b72= Te6edf3messageTff7b72.Td2a8ffcontentTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
T8b949e# Check if this is a command - if so, let command handler deal with it
Tff7b72if Te6edf3contentTff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3botTff7b72.Td2a8ffconfigTff7b72.Td2a8ffcommand_prefixTb4b4b4)Tb4b4b4:
Te6edf3command_name Tff7b72= Te6edf3contentTff7b72.Td2a8ffsplitTb4b4b4(Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Tffa657lenTb4b4b4(Te6edf3botTff7b72.Td2a8ffconfigTff7b72.Td2a8ffcommand_prefixTb4b4b4)Tb4b4b4:Tb4b4b4]
Tff7b72if Te6edf3command_name Tff7b72in Te6edf3botTff7b72.Td2a8ffcommandsTb4b4b4:
Tff7b72return Tff7b72False T8b949e# Let command handler process it
T8b949e# Not a command, echo it back
Te6edf3botTff7b72.Td2a8ffsendTb4b4b4(Te6edf3senderTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffYou said: Tffd700{Te6edf3contentTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False T8b949e# Return False to continue processing (though no commands will match)
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffhelloTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSay helloTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffhello_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffHello! This is a command response.Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
Message Handler Processing Order:
T79c0ff1. **First Message Handler** (if Te6edf3:code:Te6edf3`first_message_enabled=True` and this is first message from sender)
T79c0ff2. **General Message Handlers** (registered with Te6edf3:code:Te6edf3`@bot.on_message()`)
T79c0ff3. **Command Processing** (if message matches a registered command)
Handlers can return Te6edf3:code:Te6edf3`True` to stop further processing or Te6edf3:code:Te6edf3`False` to continue to the next stage.
Tc9d1d9Handling Events
Tc9d1d9---------------
You can register handlers for various bot events using the Te6edf3:code:Te6edf3`@bot.events.on()` decorator.
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Tff7b72from T7ee787lxmfyT7ee787.T7ee787events Tff7b72import Te6edf3EventPriority T8b949e# Optional for priority
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffEventBotTa5d6ff"Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffeventsTff7b72.Td2a8ffonTb4b4b4(Ta5d6ff"Ta5d6ffmessage_receivedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8fflog_messageTb4b4b4(Te6edf3eventTb4b4b4)Tb4b4b4:
T8b949e# Event object contains details
Te6edf3sender Tff7b72= Te6edf3eventTff7b72.Td2a8ffdataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffsenderTa5d6ff"Tb4b4b4)
Te6edf3message_content Tff7b72= Te6edf3eventTff7b72.Td2a8ffdataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4)Tff7b72.Td2a8ffcontentTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff'Ta5d6ffutf-8Ta5d6ff'Tb4b4b4, Te6edf3errorsTff7b72=Ta5d6ff'Ta5d6ffignoreTa5d6ff'Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffReceived message from Tffd700{Te6edf3senderTffd700}Ta5d6ff: Tffd700{Te6edf3message_contentTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# You can cancel event processing (e.g., stop message handling)
T8b949e# if sender == "some_blocked_hash":
T8b949e# event.cancel()
Tf0883e@botTff7b72.Td2a8ffeventsTff7b72.Td2a8ffonTb4b4b4(Ta5d6ff"Ta5d6ffcommand_executedTa5d6ff"Tb4b4b4, Te6edf3priorityTff7b72=Te6edf3EventPriorityTff7b72.Td2a8ffLOWTb4b4b4)
Tff7b72def Td2a8fflog_commandTb4b4b4(Te6edf3eventTb4b4b4)Tb4b4b4:
T8b949e# Example: event.data might contain {'command_name': 'ping', 'sender': '...', ...}
Te6edf3command_name Tff7b72= Te6edf3eventTff7b72.Td2a8ffdataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6ffcommand_nameTa5d6ff'Tb4b4b4, Ta5d6ff'Ta5d6ffunknownTa5d6ff'Tb4b4b4)
Te6edf3sender Tff7b72= Te6edf3eventTff7b72.Td2a8ffdataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff'Ta5d6ffsenderTa5d6ff'Tb4b4b4, Ta5d6ff'Ta5d6ffunknownTa5d6ff'Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffCommand Ta5d6ff'Tffd700{Te6edf3command_nameTffd700}Ta5d6ff'Ta5d6ff executed by Tffd700{Te6edf3senderTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# You can define custom events too
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffspecialTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffspecial_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3ctxTff7b72.Td2a8ffreplyTb4b4b4(Ta5d6ff"Ta5d6ffDoing something special!Ta5d6ff"Tb4b4b4)
T8b949e# Dispatch a custom event
Te6edf3botTff7b72.Td2a8ffeventsTff7b72.Td2a8ffdispatchTb4b4b4(Te6edf3EventTb4b4b4(Ta5d6ff"Ta5d6ffspecial_action_takenTa5d6ff"Tb4b4b4, Te6edf3dataTff7b72=Tb4b4b4{Ta5d6ff"Ta5d6ffuserTa5d6ff"Tb4b4b4: Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4}Tb4b4b4)Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffeventsTff7b72.Td2a8ffonTb4b4b4(Ta5d6ff"Ta5d6ffspecial_action_takenTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffhandle_specialTb4b4b4(Te6edf3eventTb4b4b4)Tb4b4b4:
Te6edf3user Tff7b72= Te6edf3eventTff7b72.Td2a8ffdataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffuserTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffSpecial action was taken by user: Tffd700{Te6edf3userTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3botTff7b72.Td2a8ffrunTb4b4b4(Tb4b4b4)
See Te6edf3:code:Te6edf3`lxmfy/events.py` for more details on the Te6edf3:code:Te6edf3`Event` structure and priorities.
Tc9d1d9Storage
Tc9d1d9-------
LXMFy provides JSON, SQLite, and In-Memory storage backends.
T79c0ff* **JSON:** Simple, human-readable. Good for small datasets. Configure with Te6edf3:code:Te6edf3`storage_type="json"` and Te6edf3:code:Te6edf3`storage_path="your_data_dir"`.
T79c0ff* **SQLite:** More efficient for larger datasets or frequent writes. Configure with Te6edf3:code:Te6edf3`storage_type="sqlite"` and Te6edf3:code:Te6edf3`storage_path="your_db_file.db"`.
T79c0ff* **Memory:** Entirely in-RAM storage. State is lost on shutdown. Configure with Te6edf3:code:Te6edf3`storage_type="memory"`.
You can access the storage interface via Te6edf3:code:Te6edf3`bot.storage`:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
T8b949e# Save data
Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffsetTb4b4b4(Ta5d6ff"Ta5d6ffuser_prefs:Ta5d6ff" Tff7b72+ Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4, Tb4b4b4{Ta5d6ff"Ta5d6ffthemeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdarkTa5d6ff"Tb4b4b4}Tb4b4b4)
T8b949e# Get data (with a default value)
Te6edf3prefs Tff7b72= Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffuser_prefs:Ta5d6ff" Tff7b72+ Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4, Tb4b4b4{Tb4b4b4}Tb4b4b4)
Te6edf3theme Tff7b72= Te6edf3prefsTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffthemeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflightTa5d6ff"Tb4b4b4)
T8b949e# Check if data exists
Tff7b72if Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffexistsTb4b4b4(Ta5d6ff"Ta5d6ffsome_keyTa5d6ff"Tb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffKey exists!Ta5d6ff"Tb4b4b4)
T8b949e# Delete data
Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffdeleteTb4b4b4(Ta5d6ff"Ta5d6ffold_data_keyTa5d6ff"Tb4b4b4)
T8b949e# Scan for keys with a prefix (useful for listing user data)
Te6edf3user_keys Tff7b72= Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffscanTb4b4b4(Ta5d6ff"Ta5d6ffuser_prefs:Ta5d6ff"Tb4b4b4)
Tff7b72for Te6edf3key Tff7b72in Te6edf3user_keysTb4b4b4:
Te6edf3user_data Tff7b72= Te6edf3botTff7b72.Td2a8ffstorageTff7b72.Td2a8ffgetTb4b4b4(Te6edf3keyTb4b4b4)
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffData for Tffd700{Te6edf3keyTffd700}Ta5d6ff: Tffd700{Te6edf3user_dataTffd700}Ta5d6ff"Tb4b4b4)
See Te6edf3:code:Te6edf3`lxmfy/storage.py` and the API reference for more details.
Tc9d1d9Permissions
Tc9d1d9-----------
LXMFy includes an optional role-based permission system. Enable it with Te6edf3:code:Te6edf3`permissions_enabled=True` during Te6edf3:code:Te6edf3`LXMFBot` initialization.
T79c0ff* **Roles:** Define roles with specific permissions (e.g., Te6edf3:code:Te6edf3`DefaultPerms.MANAGE_USERS`).
T79c0ff* **Permissions:** Granular flags defined in Te6edf3:code:Te6edf3`DefaultPerms` (e.g., Te6edf3:code:Te6edf3`USE_COMMANDS`, Te6edf3:code:Te6edf3`BYPASS_SPAM`).
T79c0ff* **Assignment:** Assign roles to user hashes.
See Te6edf3:code:Te6edf3`lxmfy/permissions.py`, the API reference, and potentially example cogs (if any are created) for usage details.
Tc9d1d9Signature Verification
Tc9d1d9----------------------
LXMFy provides configuration for LXMF's built-in cryptographic message signing and verification. All LXMF messages are automatically signed by the LXMF/RNS stack - LXMFy simply allows you to enforce signature verification policies.
**Configuration:**
Enable signature verification in your bot configuration:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffSecureBotTa5d6ff"Tb4b4b4,
Te6edf3signature_verification_enabledTff7b72=Tff7b72TrueTb4b4b4, T8b949e# Enable signature checking
Te6edf3require_message_signaturesTff7b72=Tff7b72False T8b949e# Set to True to reject unsigned messages
Tb4b4b4)
**How It Works:**
LXMF automatically handles all cryptographic operations:
T79c0ff1. **Outgoing Messages:** LXMF automatically signs all messages using the sender's RNS identity during message packing.
T79c0ff2. **Incoming Messages:** LXMF automatically validates signatures using the sender's RNS identity and provides validation results.
T79c0ff3. **LXMFy's Role:** LXMFy checks LXMF's validation results and enforces your policy:
T79c0ff- If Te6edf3:code:Te6edf3`signature_verification_enabled=False`: All messages are accepted (default)
T79c0ff- If Te6edf3:code:Te6edf3`signature_verification_enabled=True` and Te6edf3:code:Te6edf3`require_message_signatures=False`: Messages are accepted but unsigned/invalid signatures are logged
T79c0ff- If Te6edf3:code:Te6edf3`signature_verification_enabled=True` and Te6edf3:code:Te6edf3`require_message_signatures=True`: Unsigned or invalid messages are rejected
T79c0ff4. **Permission Integration:** Users with Te6edf3:code:Te6edf3`BYPASS_SPAM` permission can bypass signature verification requirements.
**CLI Management:**
You can manage signature verification settings using the CLI:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72bash
T8b949e# Test signature verification
lxmfy signatures Tffa657test
T8b949e# Enable signature verification
lxmfy signatures Tffa657enable
T8b949e# Disable signature verification
lxmfy signatures disable
**Technical Details:**
LXMF uses Ed25519 signatures provided by the RNS cryptography system. Every LXMF message includes the sender's signature, which is validated against their known RNS identity. LXMFy simply reads LXMF's Te6edf3:code:Te6edf3`message.signature_validated` property and Te6edf3:code:Te6edf3`message.unverified_reason` to enforce your bot's security policy.
Tc9d1d9Advanced Message Delivery
Tc9d1d9--------------------------
LXMFy supports advanced message delivery options for improved reliability.
Tc9d1d9Using Propagation Nodes
Tc9d1d9^^^^^^^^^^^^^^^^^^^^^^^^
Send messages through specific LXMF propagation nodes:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffPropagationBotTa5d6ff"Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffsendTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSend via propagation nodeTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffsend_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
T8b949e# Set a specific propagation node once (config-level)
Te6edf3botTff7b72.Td2a8ffset_propagation_nodeTb4b4b4(Ta5d6ff"Ta5d6ff<propagation_node_hash_here>Ta5d6ff"Tb4b4b4)
T8b949e# Send using configured delivery strategy
Te6edf3botTff7b72.Td2a8ffsendTb4b4b4(
Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4,
Ta5d6ff"Ta5d6ffThis message will use direct delivery with propagation fallback as configuredTa5d6ff"
Tb4b4b4)
Propagation nodes are useful when direct delivery is not possible or when you want to ensure message delivery through the Reticulum mesh network.
Tc9d1d9Configuring Retries
Tc9d1d9^^^^^^^^^^^^^^^^^^^
Configure automatic retry attempts for failed message deliveries via bot config:
Tb4b4b4.. Tff7b72code-blockTb4b4b4:: Tff7b72python
Tff7b72from T7ee787lxmfy Tff7b72import Te6edf3LXMFBot
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffReliableBotTa5d6ff"Tb4b4b4)
Te6edf3bot Tff7b72= Te6edf3LXMFBotTb4b4b4(
Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffReliableBotTa5d6ff"Tb4b4b4,
Te6edf3direct_delivery_retriesTff7b72=T79c0ff5Tb4b4b4, T8b949e# Retry direct delivery up to 5 times
Te6edf3propagation_fallback_enabledTff7b72=Tff7b72True
Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffimportantTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSend important message with retriesTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffimportant_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
Te6edf3botTff7b72.Td2a8ffsendTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4, Ta5d6ff"Ta5d6ffThis is an important messageTa5d6ff"Tb4b4b4)
Tf0883e@botTff7b72.Td2a8ffcommandTb4b4b4(Te6edf3nameTff7b72=Ta5d6ff"Ta5d6ffnormalTa5d6ff"Tb4b4b4, Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSend with default retriesTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffnormal_commandTb4b4b4(Te6edf3ctxTb4b4b4)Tb4b4b4:
T8b949e# Default direct_delivery_retries is 3
Te6edf3botTff7b72.Td2a8ffsendTb4b4b4(Te6edf3ctxTff7b72.Td2a8ffsenderTb4b4b4, Ta5d6ff"Ta5d6ffThis message uses default retry settingsTa5d6ff"Tb4b4b4)
The retry system:
T79c0ff- Automatically tracks delivery attempts per destination
T79c0ff- Retries failed direct deliveries up to Te6edf3:code:Te6edf3`direct_delivery_retries`
T79c0ff- Resets the retry counter on successful delivery
T79c0ff- Logs retry attempts and failures for debugging
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────